//
//  YXPlayerView.swift
//  swift_wobo
//
//  Created by yuxin on 2018/12/1.
//  Copyright © 2018 wuxiantianli. All rights reserved.
//

import UIKit
import AVFoundation

class YXPlayerView: UIView {
    private var playerItem: AVPlayerItem?{
        willSet {
            removePlayerItemObserver()
            removePlayerNotifications()
        }
        didSet {
            addPlayerItemObserver()
            addPlayerNotifications()
        }
    }
    private var player: AVPlayer? {
        willSet {
            removePlayerTimeObserver()
        }
        didSet {
            addPlayerTimeObserver()
        }
    }
    private var playerLayer : AVPlayerLayer?
    
    private var timeObserver: Any?
    
    private let bufferView = UIActivityIndicatorView()
    private let bottomView = YXPlayerBottomView()
    private let loaderManager = YXPlayerResourceLoaderManager()
    
    private var isUserPause = false
    
    lazy var playBtn: UIButton = {
       let btn = UIButton.init(type: UIButton.ButtonType.custom)
        btn.setImage(UIImage(named: "btn_home_play_p"), for: UIControl.State.selected)
        btn.addTarget(self, action: #selector(playBtnClick(btn:)), for: UIControl.Event.touchUpInside)
        return btn
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        setup()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        playerLayer?.frame = bounds
        bufferView.center = CGPoint.init(x: bounds.size.width/2, y: bounds.size.height/2)
        
        let bottomH: CGFloat = 44
        bottomView.frame = CGRect.init(x: 0, y: bounds.size.height-bottomH, width: bounds.size.width, height: bottomH)
        playBtn.frame = bounds
    }
    deinit{
        removePlayerTimeObserver()
        removePlayerItemObserver()
        removePlayerNotifications()
        
        print(#function)
    }
    
    private func setup() {
        let index: Int  = Int(arc4random())%YXPLayerConfigure.videoUrlsArr.count
        let urlStr = YXPLayerConfigure.videoUrlsArr[index]
        
        guard let videoUrl = URL.init(string: urlStr) else {
             fatalError("播放地址错误")
        }
        
//        playerItem = AVPlayerItem.init(asset: AVAsset(url: videoUrl))
        
        playerItem = loaderManager.customPlayerItem(videoUrl)
        
        player = AVPlayer.init(playerItem: playerItem)
        playerLayer = AVPlayerLayer.init(player: player)
        layer.addSublayer(playerLayer!)
        
        bufferView.style = .whiteLarge
        bufferView.hidesWhenStopped = true
        addSubview(bufferView)
        bufferView.startAnimating()
        
        addSubview(playBtn)
        addSubview(bottomView)
        
        
        bottomView.progressView.addTarget(self, action: #selector(sliderTouchDown(slider:)), for: UIControl.Event.touchDown)
         bottomView.progressView.addTarget(self, action: #selector(sliderTouchValueChange(slider:)), for: UIControl.Event.valueChanged)
        bottomView.progressView.addTarget(self, action: #selector(sliderTouchUp(slider:)), for: UIControl.Event.touchUpInside)
        bottomView.progressView.addTarget(self, action: #selector(sliderTouchUp(slider:)), for: UIControl.Event.touchUpOutside)
    }
    
    func play() {
        player?.play()
    }
    
    func pasue() {
        player?.pause()
    }
    
    @objc private func sliderTouchDown(slider: UISlider) {
       print(slider.value)
    }
    @objc private func sliderTouchValueChange(slider: UISlider) {
//        print(slider.value)
    }
    
    @objc private func sliderTouchUp(slider: UISlider) {
        print(slider.value)
        if let itemDuration = playerItem?.duration.seconds {
            let  toTime = itemDuration * Double(slider.value)
            seekTime(toTime)
        }
        
    }
    
    @objc private func playBtnClick(btn: UIButton) {
        if isUserPause {
            btn.isSelected = false
            isUserPause = false
            play()
        }else {
            btn.isSelected = true
            isUserPause = true
            pasue()
        }
    }
}


// MARK: - oberver
extension YXPlayerView {
    private func addPlayerNotifications() {
        NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidPlayToEndTime), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
    }
    
    private func removePlayerNotifications() {
        NotificationCenter.default.removeObserver(self)
    }
    
    private func addPlayerTimeObserver() {
        timeObserver = player?.addPeriodicTimeObserver(forInterval: .init(value: 1, timescale: 1), queue: DispatchQueue.main, using: { [weak self] time in
            guard let strongSelf = self else { return }
            if let currentTime = strongSelf.player?.currentTime().seconds, let totalDuration = strongSelf.player?.currentItem?.duration.seconds {
//                print("currentTime:\(currentTime),total:\(totalDuration)")
                strongSelf.handlePlayerDurationDidChange(currentTime, totalDuration: totalDuration)
            }
        })
    }
    
    private func removePlayerTimeObserver() {
        if let timer = timeObserver {
            player?.removeTimeObserver(timer)
        }
    }
    
    private func addPlayerItemObserver() {
        playerItem?.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.loadedTimeRanges), options: .new, context: nil)
        playerItem?.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), options: .new, context: nil)
        playerItem?.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.playbackBufferEmpty), options: .new, context: nil)
        //缓存可以支持播放的时候调用()
        playerItem?.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.playbackLikelyToKeepUp), options: .new, context: nil)
    }
    private func removePlayerItemObserver() {
        playerItem?.removeObserver(self, forKeyPath: #keyPath(AVPlayerItem.loadedTimeRanges))
        playerItem?.removeObserver(self, forKeyPath: #keyPath(AVPlayerItem.status))
        playerItem?.removeObserver(self, forKeyPath: #keyPath(AVPlayerItem.playbackBufferEmpty))
        playerItem?.removeObserver(self, forKeyPath: #keyPath(AVPlayerItem.playbackLikelyToKeepUp))
    
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        guard (object as? AVPlayerItem) != nil else { return }
        if keyPath == #keyPath(AVPlayerItem.loadedTimeRanges) {
           handlePlayerItemLoadTimeRanges()
        }else if keyPath == #keyPath(AVPlayerItem.status) {
            handlePlayerItemStatusChange(change: change)
        }else if keyPath == #keyPath(AVPlayerItem.playbackBufferEmpty) {
            handlePalyerItemPlaybackBufferEmpty(change: change)
        }else if keyPath == #keyPath(AVPlayerItem.playbackLikelyToKeepUp) {
           handlePlayerItemPlaybackLikelyToKeepUp(change: change)
        }else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        }
    }
}

//MARK: - Notifation Selector & KVO
extension YXPlayerView {
    
    func handlePlayerDurationDidChange(_ currentDuration: TimeInterval, totalDuration: TimeInterval) {
        var current = formatSecondsToString(currentDuration)
        if totalDuration.isNaN {  // HLS
            current = "00:00"
        }
        let total = formatSecondsToString(totalDuration)
        bottomView.currLabel.text = current
        bottomView.totalLabel.text = total
        bottomView.progressView.value = Float(currentDuration/totalDuration)
        
    }
    
    private func handlePlayerItemStatusChange(change: [NSKeyValueChangeKey : Any]?) {
        let status: AVPlayerItem.Status
        if let statusNum = change?[NSKeyValueChangeKey.newKey] as? NSNumber {
            status = AVPlayerItem.Status(rawValue: statusNum.intValue)!
        }else{
            status = .unknown
        }
        
        switch status {
        case .unknown:
            bufferView.startAnimating()
        case .readyToPlay:
            print("播起来了")
            bufferView.stopAnimating()
        case .failed:
            bufferView.stopAnimating()
            print("加载异常")
        }
    }
    
    private func handlePlayerItemLoadTimeRanges() {
        
        let loadedTimeRanges = player?.currentItem?.loadedTimeRanges
        if let bufferTimeRange = loadedTimeRanges?.first?.timeRangeValue {
            let star = bufferTimeRange.start.seconds         // The start time of the time range.
            let duration = bufferTimeRange.duration.seconds  // The duration of the time range.
            let bufferTime = star + duration
            
            if let itemDuration = playerItem?.duration.seconds {
//                print("currentBuffer:\(bufferTime),total:\(itemDuration)")
                bottomView.bufferView.progress = Float(bufferTime/itemDuration)
            }
//            if let currentTime = playerItem?.currentTime().seconds{
//                print("currentPlayTime:\(currentTime)")
                
//            }
            
        } else {
            
        }
    }
    
    private func handlePalyerItemPlaybackBufferEmpty(change: [NSKeyValueChangeKey : Any]?) {
        if let bufferEmpty = change?[NSKeyValueChangeKey.newKey] as? Bool {
            if bufferEmpty {
                bufferView.startAnimating()
            }
        }
        
    }
    
    private func handlePlayerItemPlaybackLikelyToKeepUp(change: [NSKeyValueChangeKey : Any]?) {
        if let canPlay = change?[NSKeyValueChangeKey.newKey] as? Bool {
            if canPlay, !isUserPause {
                play()
            }
        }
    }
    
    @objc private func playerItemDidPlayToEndTime() {
        print(#function)
    }
    
    func seekTime(_ toTime: TimeInterval) {
        if toTime.isNaN || playerItem?.status != .readyToPlay {
            return
        }
        
        DispatchQueue.main.async { [weak self]  in
            guard let strongSelf = self else { return }
            strongSelf.pasue()
            strongSelf.bufferView.startAnimating()
            strongSelf.playerItem?.seek(to: CMTimeMakeWithSeconds(toTime, preferredTimescale: Int32(NSEC_PER_SEC)), completionHandler: { (finished) in
                DispatchQueue.main.async {
                    strongSelf.bufferView.stopAnimating()
                    strongSelf.play()
                    strongSelf.playBtn.isSelected = false
                    strongSelf.isUserPause = false
                }
            })
        }
    }
    
}

extension YXPlayerView {
     private func formatSecondsToString(_ seconds: TimeInterval) -> String {
        if seconds.isNaN{
            return "00:00"
        }
        let interval = Int(seconds)
        let sec = Int(seconds.truncatingRemainder(dividingBy: 60))
        let min = interval / 60
        return String(format: "%02d:%02d", min, sec)
    }
}
